package com.jxin.mall.middle.util.redis;

import com.jxin.mall.core.util.ProtoStuffUtil;
import org.apache.commons.collections4.MapUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * protoStuffRedis 工具类
 * @author  蔡佳新
 * @version 1.0
 * @since   jdk 1.8
 */
@Component("strProtoRedisUtil")
public class StrProtoRedisUtil extends RedisUtil{
    @Resource(name = "protobufRedisTemplate")
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }




    //================================================String============================================================

    /**
     * 普通缓存获取  指定转义类型
     * @param  key   键
     * @param  clazz 转义类型
     * @return 值
     * @author 蔡佳新
     */
    @Nullable
    public  <T> T get(@NotNull String key, @NotNull Class<T> clazz) {
        Assert.notNull(key, RedisUtil.RedisUtilMsg.NON_NULL_KEY_REQUIRED);
        Assert.notNull(clazz, RedisUtil.RedisUtilMsg.NON_NULL_CLAZZ_REQUIRED);
        return ProtoStuffUtil.deserialize((byte[]) getRedisTemplate().opsForValue().get(key), clazz);
    }
    //================================================Map===============================================================

    /**
     * HashGet 带转义
     * @param  key     键 不能为null
     * @param  hashKey 项 不能为null
     * @param  clazz   项 转义类型
     * @return 值
     * @author 蔡佳新
     */
    @Nullable
    public <T> T hget(@NotNull String key, @NotNull String hashKey, @NotNull Class<T> clazz) {
        Assert.notNull(key, RedisUtil.RedisUtilMsg.NON_NULL_KEY_REQUIRED);
        Assert.notNull(hashKey, RedisUtil.RedisUtilMsg.NON_NULL_HASH_KEY_REQUIRED);
        Assert.notNull(clazz, RedisUtil.RedisUtilMsg.NON_NULL_CLAZZ_REQUIRED);
        return ProtoStuffUtil.deserialize((byte[]) getRedisTemplate().opsForHash().get(key, hashKey), clazz);
    }
    /**
     * 获取hashKey对应的所有键值
     * @param  key 键
     * @return 对应的多个键值, 不存在则为null
     * @author 蔡佳新
     */
    @Nullable
    public Map<String, Object> hmget(@NotNull String key) {
        Assert.notNull(key, RedisUtil.RedisUtilMsg.NON_NULL_KEY_REQUIRED);
        final Map<Object, Object> entries = getRedisTemplate().opsForHash().entries(key);
        if(MapUtils.isEmpty(entries)){
            return null;
        }
        final Map<String, Object> result = new HashMap<>(entries.size());
        entries.forEach((k,v)-> result.put(String.valueOf(k), v));
        return result;
    }
    /**
     * 获取hashKey对应的所有键值
     * @param  key   键
     * @param  clazz 转义类型
     * @return 对应的多个键值, 不存在则为null
     * @author 蔡佳新
     */
    @Nullable
    public <T> Map<String, T> hmget(@NotNull String key, @NotNull Class<T> clazz) {
        Assert.notNull(key, RedisUtil.RedisUtilMsg.NON_NULL_KEY_REQUIRED);
        Assert.notNull(clazz, RedisUtil.RedisUtilMsg.NON_NULL_CLAZZ_REQUIRED);
        final Map<Object, Object> entries = getRedisTemplate().opsForHash().entries(key);
        if(MapUtils.isEmpty(entries)){
            return null;
        }
        final Map<String, T> result = new HashMap<>(entries.size());
        entries.forEach((k,v)-> result.put(String.valueOf(k), ProtoStuffUtil.deserialize((byte[]) v, clazz)));
        return result;

    }
    //================================================Set===============================================================

    /**
     * 根据key获取Set中的所有值,指定转换类型
     * @param  key   键
     * @param  clazz 转换类型
     * @return 值
     * @author 蔡佳新
     */
    @Nullable
    public <T> Set<T> sGet(@NotNull String key, @NotNull Class<T> clazz) {
        Assert.notNull(key, RedisUtil.RedisUtilMsg.NON_NULL_KEY_REQUIRED);
        Assert.notNull(clazz, RedisUtil.RedisUtilMsg.NON_NULL_CLAZZ_REQUIRED);
        final Set<Object> set = getRedisTemplate().opsForSet().members(key);
        if(CollectionUtils.isEmpty(set)){
            return null;
        }
        return set.stream().map(value -> ProtoStuffUtil.deserialize((byte[]) value, clazz)).collect(Collectors.toSet());
    }

    //================================================list===============================================================

    /**
     * 获取list缓存的内容
     * @param  key   键
     * @param  start 开始
     * @param  end   结束  0 到 -1代表所有值
     * @param  clazz 转换类型
     * @return list列表
     * @author 蔡佳新
     */
    @Nullable
    public  <T> List<T> lGet(@NotNull String key, long start, long end, @NotNull Class<T> clazz) {
        Assert.notNull(key, RedisUtil.RedisUtilMsg.NON_NULL_KEY_REQUIRED);
        Assert.notNull(clazz, RedisUtil.RedisUtilMsg.NON_NULL_CLAZZ_REQUIRED);
        final List<Object> range = getRedisTemplate().opsForList().range(key, start, end);
        if(CollectionUtils.isEmpty(range)){
            return null;
        }
        return range.stream().map(value -> ProtoStuffUtil.deserialize((byte[]) value, clazz)).collect(Collectors.toList());
    }
    /**
     * 通过索引 获取list中的值,并转义
     * @param  key   键
     * @param  index 索引  index>=0时， 0 表头，1 第二个元素，依次类推；index<0时，-1，表尾，-2倒数第二个元素，依次类推
     * @param  clazz 转义的类型
     * @return 指定位置元素对象
     * @author 蔡佳新
     */
    @Nullable
    public  <T> T lGetIndex(@NotNull String key, long index, @NotNull Class<T> clazz) {
        Assert.notNull(key, RedisUtil.RedisUtilMsg.NON_NULL_KEY_REQUIRED);
        Assert.notNull(clazz, RedisUtil.RedisUtilMsg.NON_NULL_CLAZZ_REQUIRED);
        return ProtoStuffUtil.deserialize((byte[]) getRedisTemplate().opsForList().index(key, index), clazz);
    }
}
